// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package modload

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"unicode"

	"cmd/go/internal/base"
	"cmd/go/internal/cfg"
	"cmd/go/internal/fsys"
	"cmd/go/internal/lockedfile"
	"cmd/go/internal/modfetch"
	"cmd/go/internal/par"
	"cmd/go/internal/trace"

	"golang.org/x/mod/modfile"
	"golang.org/x/mod/module"
	"golang.org/x/mod/semver"
)

const (
	// 窄带版本v是Go版本（加上前导的“v”），在该版本中，
	// module module“all”模式不再关闭主模块之外的
	// 测试的依赖项。
	narrowAllVersionV = "v1.16"

	// explicititinDirectVersionV是Go版本（加上前导的“v”），在该版本中，
	// 模块的Go。mod file预计会列出每个
	// 模块的明确要求，这些模块提供由该模块传递导入的任何包。
	// 
	// 可以安全地从
	// 模块图中删除此类模块的其他间接依赖项；参见https:
	ExplicitIndirectVersionV = "v1.17"

	// separateIndirectVersionV是Go版本（加前导“v”），其中
	// “
	// 个。请参阅https:
	separateIndirectVersionV = "v1.17"
)

// ReadModFile读取并解析gomod上的mod文件。ReadModFile正确应用
// 覆盖，在读取时锁定文件，并应用修复（如果适用）。
func ReadModFile(gomod string, fix modfile.VersionFixer) (data []byte, f *modfile.File, err error) {
	if gomodActual, ok := fsys.OverlayPath(gomod); ok {
		// 如果go.mod是覆盖的一部分，请不要锁定。
		// 在计划9中，锁定需要chmod，我们不想修改任何文件EFG
		data, err = os.ReadFile(gomodActual)
	} else {
		data, err = lockedfile.Read(gomodActual)
	}
	if err != nil {
		return nil, nil, err
	}

	f, err = modfile.Parse(gomod, data, fix)
	if err != nil {
		// modfile返回错误。解析从file:line开始。
		return nil, nil, fmt.Errorf("errors parsing go.mod:\n%s\n", err)
	}
	if f.Module == nil {
		// 没有模块声明。必须添加模块路径。
		return nil, nil, errors.New("no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod")
	}

	return data, f, err
}

// modFileGoVersion返回解释modFile中
// 要求的（非空）Go版本，如果modFile为零，则返回最新的Go版本。
func modFileGoVersion(modFile *modfile.File) string {
	if modFile == nil {
		return LatestGoVersion()
	}
	if modFile.Go == nil || modFile.Go.Version == "" {
		// 主模块必须要通过。mod file，并且该文件缺少
		// /'go'指令。“go”命令自go 1.12开始自动添加指令
		// ，因此该模块的日期为go 1.11或
		// 被错误地手工编辑。
		// 
		// 围棋的语义。mod文件与Go 1.11 
		// 到Go 1.16大致相同，在1.17处更改以支持模块图修剪。
		// 所以即使是一次尝试。没有'go'指令的mod文件理论上是一个
		// go 1.11文件，脚本可能会认为它最终是一个go 1.16模块。
		return "1.16"
	}
	return modFile.Go.Version
}

// modFileIndex是在特定时间点对应于modFile的数据索引。
type modFileIndex struct {
	data         []byte
	dataNeedsFix bool // 如果fixVersion在分析数据时应用了更改，则为true 
	module       module.Version
	goVersionV   string // 前缀为“v”的GoVersion 
	require      map[module.Version]requireMeta
	replace      map[module.Version]module.Version
	exclude      map[module.Version]bool
}

type requireMeta struct {
	indirect bool
}

// ModPrunning表示Go 1.17依赖项
// 的可传递依赖项是否从以给定模块为根的模块子图中删除。
// golang.org/ref/mod#graph-修剪。）
type modPruning uint8

const (
	pruned    modPruning = iota // 删除go 1.17及更高版本模块的可传递依赖
	unpruned                    // 未删除可传递依赖
	workspace                   // 删除工作区中模块的并集
)

func pruningForGoVersion(goVersion string) modPruning {
	if semver.Compare("v"+goVersion, ExplicitIndirectVersionV) < 0 {
		// 删除go。mod file不复制有关可传递
		// 依赖项的相关信息，因此无法删除它们。
		return unpruned
	}
	return pruned
}

// CheckAllowed返回一个错误，如果m被
// 主模块的go排除，则该错误相当于ErrDisallowed。修改或被作者撤回。大多数版本查询都使用
// 这可以过滤掉不应该使用的版本。
func CheckAllowed(ctx context.Context, m module.Version) error {
	if err := CheckExclusions(ctx, m); err != nil {
		return err
	}
	if err := CheckRetractions(ctx, m); err != nil {
		return err
	}
	return nil
}

// ErrDisallowed由传递给Query的版本谓词和类似的
// 函数返回，以指示不应考虑版本。
var ErrDisallowed = errors.New("disallowed module version")

// 如果模块m被主模块的go排除，则CheckExclutions返回一个与ErrDisallowed等效的错误。mod文件。
func CheckExclusions(ctx context.Context, m module.Version) error {
	for _, mainModule := range MainModules.Versions() {
		if index := MainModules.Index(mainModule); index != nil && index.exclude[m] {
			return module.VersionError(m, errExcluded)
		}
	}
	return nil
}

var errExcluded = &excludedError{}

type excludedError struct{}

func (e *excludedError) Error() string     { return "excluded by go.mod" }
func (e *excludedError) Is(err error) bool { return err == ErrDisallowed }

// 如果模块m已被
// 其作者收回，则CheckRetractions返回错误。
func CheckRetractions(ctx context.Context, m module.Version) (err error) {
	defer func() {
		if retractErr := (*ModuleRetractedError)(nil); err == nil || errors.As(err, &retractErr) {
			return
		}
		// 将错误归因于正在检查的版本，而不是
		// 中要加载收回的版本。
		if mErr := (*module.ModuleError)(nil); errors.As(err, &mErr) {
			err = mErr.Err
		}
		err = &retractionLoadingError{m: m, err: err}
	}()

	if m.Version == "" {
		// 主模块、标准库或文件替换模块。
		// 无法收回。
		return nil
	}
	if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
		// 该模块的所有版本均已更换。
		// 不要加载缩回，因为我们只加载替换件。
		return nil
	}

	// 查找该模块的最新可用版本，并加载其go。摩登派青年如果
	// 最新版本被替换，我们将加载替换版本。
	// 
	// 如果加载go时出错。国防部，我们在这里归还。这些错误
	// 通常应该被呼叫者忽略，因为它们经常发生在
	// 我们脱机时。这些错误并不等同于ErrDisallowed，因此它们是
	// 可以与收回错误区分开来。
	// 
	// 我们在这里加载原始文件：go。mod file可能有一个不同的模块
	// 路径，如果该模块或其存储库被重命名，我们将使用该路径。
	// 我们仍然希望对模块的其他别名应用缩回。
	rm, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
	if err != nil {
		return err
	}
	summary, err := rawGoModSummary(rm)
	if err != nil {
		return err
	}

	var rationale []string
	isRetracted := false
	for _, r := range summary.retract {
		if semver.Compare(r.Low, m.Version) <= 0 && semver.Compare(m.Version, r.High) <= 0 {
			isRetracted = true
			if r.Rationale != "" {
				rationale = append(rationale, r.Rationale)
			}
		}
	}
	if isRetracted {
		return module.VersionError(m, &ModuleRetractedError{Rationale: rationale})
	}
	return nil
}

type ModuleRetractedError struct {
	Rationale []string
}

func (e *ModuleRetractedError) Error() string {
	msg := "retracted by module author"
	if len(e.Rationale) > 0 {
		// 这是一个打印在终端上的短错误，所以只需打印第一个基本原理即可。
		msg += ": " + ShortMessage(e.Rationale[0], "retracted by module author")
	}
	return msg
}

func (e *ModuleRetractedError) Is(err error) bool {
	return err == ErrDisallowed
}

type retractionLoadingError struct {
	m   module.Version
	err error
}

func (e *retractionLoadingError) Error() string {
	return fmt.Sprintf("loading module retractions for %v: %v", e.m, e.err)
}

func (e *retractionLoadingError) Unwrap() error {
	return e.err
}

// ShortMessage从go返回一个字符串。可以在终端中安全打印的mod（例如，收回
// 基本原理或弃用消息）。
// 
// 如果给定的字符串为空，ShortMessage将返回给定的默认值。如果给定字符串太长或包含不可打印的字符，短消息
// 将返回硬编码字符串。
func ShortMessage(message, emptyDefault string) string {
	const maxLen = 500
	if i := strings.Index(message, "\n"); i >= 0 {
		message = message[:i]
	}
	message = strings.TrimSpace(message)
	if message == "" {
		return emptyDefault
	}
	if len(message) > maxLen {
		return "(message omitted: too long)"
	}
	for _, r := range message {
		if !unicode.IsGraphic(r) && !unicode.IsSpace(r) {
			return "(message omitted: contains non-printable characters)"
		}
	}
	// 注意：开始。mod parser拒绝无效的UTF-8，所以我们这里不检查它。
	return message
}

// CheckDeprecation从go返回一条deprecation消息。
// /给定模块的最新版本的mod文件。弃用消息是以
// “弃用：”开头的模块指令之前或同一行上的注释，并一直运行到本段末尾。
// 
// CheckDeprecation在无法加载消息时返回错误。
// 检查折旧返回“”，如果没有折旧消息，则为零。
func CheckDeprecation(ctx context.Context, m module.Version) (deprecation string, err error) {
	defer func() {
		if err != nil {
			err = fmt.Errorf("loading deprecation for %s: %w", m.Path, err)
		}
	}()

	if m.Version == "" {
		// 主模块、标准库或文件替换模块。
		// 不要查看贬损。
		return "", nil
	}
	if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
		// 模块的所有版本都已更换。
		// 我们将单独查找替换品的折旧率。
		return "", nil
	}

	latest, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
	if err != nil {
		return "", err
	}
	summary, err := rawGoModSummary(latest)
	if err != nil {
		return "", err
	}
	return summary.deprecated, nil
}

func replacement(mod module.Version, replace map[module.Version]module.Version) (fromVersion string, to module.Version, ok bool) {
	if r, ok := replace[mod]; ok {
		return mod.Version, r, true
	}
	if r, ok := replace[module.Version{Path: mod.Path}]; ok {
		return "", r, true
	}
	return "", module.Version{}, false
}

// Replacement返回mod的替换项（如果有）。如果路径在
// 模块中。版本是相对的它是相对于
// 工作区模式之外的单个主模块，或工作区模式下的工作区目录。
func Replacement(mod module.Version) module.Version {
	foundFrom, found, foundModRoot := "", module.Version{}, ""
	if MainModules == nil {
		return module.Version{}
	} else if MainModules.Contains(mod.Path) && mod.Version == "" {
		// 不要替换主模块的工作区版本。
		return module.Version{}
	}
	if _, r, ok := replacement(mod, MainModules.WorkFileReplaceMap()); ok {
		return r
	}
	for _, v := range MainModules.Versions() {
		if index := MainModules.Index(v); index != nil {
			if from, r, ok := replacement(mod, index.replace); ok {
				modRoot := MainModules.ModRoot(v)
				if foundModRoot != "" && foundFrom != from && found != r {
					base.Errorf("conflicting replacements found for %v in workspace modules defined by %v and %v",
						mod, modFilePath(foundModRoot), modFilePath(modRoot))
					return canonicalizeReplacePath(found, foundModRoot)
				}
				found, foundModRoot = r, modRoot
			}
		}
	}
	return canonicalizeReplacePath(found, foundModRoot)
}

func replaceRelativeTo() string {
	if workFilePath := WorkFilePath(); workFilePath != "" {
		return filepath.Dir(workFilePath)
	}
	return MainModules.ModRoot(MainModules.mustGetSingleMainModule())
}

// CanonicalizerReplacePath确保磁盘上被替换的相对模块路径
// 相对于工作区目录（在工作区模式下）或模块的
// 目录（在模块模式下，它们已经存在）。
func canonicalizeReplacePath(r module.Version, modRoot string) module.Version {
	if filepath.IsAbs(r.Path) || r.Version != "" {
		return r
	}
	workFilePath := WorkFilePath()
	if workFilePath == "" {
		return r
	}
	abs := filepath.Join(modRoot, r.Path)
	if rel, err := filepath.Rel(filepath.Dir(workFilePath), abs); err == nil {
		return module.Version{Path: rel, Version: r.Version}
	}
	// 我们无法使版本的路径相对于工作区的路径，因此只需返回绝对路径即可。这是我们能做的最好的了。
	return module.Version{Path: abs, Version: r.Version}
}

// resolveReplacement返回实际用于加载源代码的模块
// for m:m本身或m的替换（如果m被替换）。
// 如果找到一个模块，它还会返回提供替换的模块的modroot。
func resolveReplacement(m module.Version) module.Version {
	if r := Replacement(m); r.Path != "" {
		return r
	}
	return m
}

func toReplaceMap(replacements []*modfile.Replace) map[module.Version]module.Version {
	replaceMap := make(map[module.Version]module.Version, len(replacements))
	for _, r := range replacements {
		if prev, dup := replaceMap[r.Old]; dup && prev != r.New {
			base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New)
		}
		replaceMap[r.Old] = r.New
	}
	return replaceMap
}

// indexModFile重建modFile的索引。
// 如果modFile在第一次读取后已被更改，则
// modFile。必须在indexModFile之前调用Cleanup。
func indexModFile(data []byte, modFile *modfile.File, mod module.Version, needsFix bool) *modFileIndex {
	i := new(modFileIndex)
	i.data = data
	i.dataNeedsFix = needsFix

	i.module = module.Version{}
	if modFile.Module != nil {
		i.module = modFile.Module.Mod
	}

	i.goVersionV = ""
	if modFile.Go == nil {
		rawGoVersion.Store(mod, "")
	} else {
		// 我们将使用semver软件包来比较Go版本，所以请继续
		// 并添加它期望的“v”前缀一次，而不是每次。
		i.goVersionV = "v" + modFile.Go.Version
		rawGoVersion.Store(mod, modFile.Go.Version)
	}

	i.require = make(map[module.Version]requireMeta, len(modFile.Require))
	for _, r := range modFile.Require {
		i.require[r.Mod] = requireMeta{indirect: r.Indirect}
	}

	i.replace = toReplaceMap(modFile.Replace)

	i.exclude = make(map[module.Version]bool, len(modFile.Exclude))
	for _, x := range modFile.Exclude {
		i.exclude[x.Mod] = true
	}

	return i
}

// modFileIsDirty报告是否运行。mod file与索引的文件有很大不同。
// 如果modFile在第一次读取后已被更改（甚至是修饰），则
// modFile。必须在modFileIsDirty之前调用Cleanup。快走。mod文件并不总是需要“go”版本，所以如果缺少一个，请不要出错
func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool {
	if i == nil {
		return modFile != nil
	}

	if i.dataNeedsFix {
		return true
	}

	if modFile.Module == nil {
		if i.module != (module.Version{}) {
			return true
		}
	} else if modFile.Module.Mod != i.module {
		return true
	}

	if modFile.Go == nil {
		if i.goVersionV != "" {
			return true
		}
	} else if "v"+modFile.Go.Version != i.goVersionV {
		if i.goVersionV == "" && cfg.BuildMod != "mod" {
			// 我们可能在模块
			// 缓存中的一个旧模块中，应该偏向于提供有用的行为。
		} else {
			return true
		}
	}

	if len(modFile.Require) != len(i.require) ||
		len(modFile.Replace) != len(i.replace) ||
		len(modFile.Exclude) != len(i.exclude) {
		return true
	}

	for _, r := range modFile.Require {
		if meta, ok := i.require[r.Mod]; !ok {
			return true
		} else if r.Indirect != meta.indirect {
			if cfg.BuildMod == "readonly" {
				// 模块要求一致；只有“
				// 注释是错误的。但这些注释只保证在“go mod tidy”之后是准确的“-在提交更改之前运行这些是一个好主意，但它肯定不是强制性的。
			} else {
				return true
			}
		}
	}

	for _, r := range modFile.Replace {
		if r.New != i.replace[r.Old] {
			return true
		}
	}

	for _, x := range modFile.Exclude {
		if !i.exclude[x.Mod] {
			return true
		}
	}

	return false
}

// rawGoVersion记录从每个模块的Go.mod文件解析的Go版本。
// 
// 如果模块被替换，替换版本由
// replacement module version输入，而不是被替换的版本放置。
var rawGoVersion sync.Map // map[module.Version]string 

// modFileSummary是go的摘要。mod文件，我们不需要
// 保留完整信息-例如，go。依赖项
// 模块的mod文件。
type modFileSummary struct {
	module     module.Version
	goVersion  string
	pruning    modPruning
	require    []module.Version
	retract    []retraction
	deprecated string
}

// 收回包括收回版本间隔和基本原理。
// 收回就像modfile。收回，但它不指向语法树。
type retraction struct {
	modfile.VersionInterval
	Rationale string
}

// goModSummary返回go的摘要。模块m的mod文件，
// 考虑到m的任何替换、其依赖项的排除、
// 和/或卖方。
// 
// m必须是模块图中的版本，可以从目标模块访问。
// 在只读模式下，go。sum文件必须包含一个m's go的条目。mod file 
// /（或其替代品）。不能为目标模块
// 本身调用goModSummary，因为其要求可能会更改。其他
// 模块版本使用rawGoModSummary。
// 
// 调用者不得修改返回的摘要。
func goModSummary(m module.Version) (*modFileSummary, error) {
	if m.Version == "" && !inWorkspaceMode() && MainModules.Contains(m.Path) {
		panic("internal error: goModSummary called on a main module")
	}

	if cfg.BuildMod == "vendor" {
		summary := &modFileSummary{
			module: module.Version{Path: m.Path},
		}
		if vendorVersion[m.Path] != m.Version {
			// 此模块未出售，因此无法从中加载包，
			// 它与构建无关。
			return summary, nil
		}

		// 对于目标以外的每个模块，
		// 返回模块的完整列表。txt。
		readVendorList(MainModules.mustGetSingleMainModule())

		// 我们不知道卖方模块实际依赖的版本，所以假设它需要所有东西。
		summary.require = vendorList
		return summary, nil
	}

	actual := resolveReplacement(m)
	if HasModRoot() && cfg.BuildMod == "readonly" && !inWorkspaceMode() && actual.Version != "" {
		key := module.Version{Path: actual.Path, Version: actual.Version + "/go.mod"}
		if !modfetch.HaveSum(key) {
			suggestion := fmt.Sprintf("; to add it:\n\tgo mod download %s", m.Path)
			return nil, module.VersionError(actual, &sumMissingError{suggestion: suggestion})
		}
	}
	summary, err := rawGoModSummary(actual)
	if err != nil {
		return nil, err
	}

	if actual.Version == "" {
		// 实际模块是一个文件系统本地替换，为此我们有
		// 不幸的是，没有强制执行任何关于模块行或
		// 匹配模块路径的不变量。什么都可以。
		// 
		// TODO（bcmills）：删除此特殊情况，更新测试，并添加
		// 发行说明。
	} else {
		if summary.module.Path == "" {
			return nil, module.VersionError(actual, errors.New("parsing go.mod: missing module line"))
		}

		// 理论上，如果我们获取的
		// 版本没有明确的go，我们应该只允许mpath不等于m.Path。mod文件：如果该文件运行。mod file 
		// 是显式的，那么它应该完全匹配（以确保正确解释模块内其他
		// 包的导入）。不幸的是，我们无法确定模块代理协议中的信息：我们将让
		// 在我们从
		// 模块中加载实际包时保留该验证。
		if mpath := summary.module.Path; mpath != m.Path && mpath != actual.Path {
			return nil, module.VersionError(actual, fmt.Errorf(`parsing go.mod:
	module declares its path as: %s
	        but was required as: %s`, mpath, m.Path))
		}
	}

	for _, mainModule := range MainModules.Versions() {
		if index := MainModules.Index(mainModule); index != nil && len(index.exclude) > 0 {
			// 放弃对排除版本的任何要求。zhong zhong zhong
			haveExcludedReqs := false
			for _, r := range summary.require {
				if index.exclude[r] {
					haveExcludedReqs = true
					break
				}
			}
			if haveExcludedReqs {
				s := new(modFileSummary)
				*s = *summary
				s.require = make([]module.Version, 0, len(summary.require))
				for _, r := range summary.require {
					if !index.exclude[r] {
						s.require = append(s.require, r)
					}
				}
				summary = s
			}
		}
	}
	return summary, nil
}

// rawGoModSummary返回go的新摘要。模块m的mod文件，
// 忽略可能适用于m的所有替换，并排除可能适用于
// 其依赖项的替换。
// 
// rawGoModSummary不能在目标模块上使用。

func rawGoModSummary(m module.Version) (*modFileSummary, error) {
	if m.Path == "" && MainModules.Contains(m.Path) {
		panic("internal error: rawGoModSummary called on the Target module")
	}

	type key struct {
		m module.Version
	}
	type cached struct {
		summary *modFileSummary
		err     error
	}
	c := rawGoModSummaryCache.Do(key{m}, func() any {
		summary := new(modFileSummary)
		name, data, err := rawGoModData(m)
		if err != nil {
			return cached{nil, err}
		}
		f, err := modfile.ParseLax(name, data, nil)
		if err != nil {
			return cached{nil, module.VersionError(m, fmt.Errorf("parsing %s: %v", base.ShortPath(name), err))}
		}
		if f.Module != nil {
			summary.module = f.Module.Mod
			summary.deprecated = f.Module.Deprecated
		}
		if f.Go != nil && f.Go.Version != "" {
			rawGoVersion.LoadOrStore(m, f.Go.Version)
			summary.goVersion = f.Go.Version
			summary.pruning = pruningForGoVersion(f.Go.Version)
		} else {
			summary.pruning = unpruned
		}
		if len(f.Require) > 0 {
			summary.require = make([]module.Version, 0, len(f.Require))
			for _, req := range f.Require {
				summary.require = append(summary.require, req.Mod)
			}
		}
		if len(f.Retract) > 0 {
			summary.retract = make([]retraction, 0, len(f.Retract))
			for _, ret := range f.Retract {
				summary.retract = append(summary.retract, retraction{
					VersionInterval: ret.VersionInterval,
					Rationale:       ret.Rationale,
				})
			}
		}

		return cached{summary, nil}
	}).(cached)

	return c.summary, c.err
}

var rawGoModSummaryCache par.Cache // 模块。版本→ rawGoModSummary result 

// rawGoModData返回go的内容。模块m的mod文件，忽略
// 可能适用于m的所有替换。
// 
// rawGoModData不能在目标模块上使用。与rawGoModSummary不同，rawGoModData不会将其结果缓存在内存中。
// 除非您特别需要这些字节，否则请改用rawGoModSummary。
func rawGoModData(m module.Version) (name string, data []byte, err error) {
	if m.Version == "" {
		// m是一个只有文件路径的替换模块。

		dir := m.Path
		if !filepath.IsAbs(dir) {
			if inWorkspaceMode() && MainModules.Contains(m.Path) {
				dir = MainModules.ModRoot(m)
			} else {
				dir = filepath.Join(replaceRelativeTo(), dir)
			}
		}
		name = filepath.Join(dir, "go.mod")
		if gomodActual, ok := fsys.OverlayPath(name); ok {
			// 不要锁定go。如果它是覆盖层的一部分，则进行修改。
			// 在计划9中，锁定需要chmod，我们不想修改覆盖中的任何文件
			// 。见#44700。
			data, err = os.ReadFile(gomodActual)
		} else {
			data, err = lockedfile.Read(gomodActual)
		}
		if err != nil {
			return "", nil, module.VersionError(m, fmt.Errorf("reading %s: %v", base.ShortPath(name), err))
		}
	} else {
		if !semver.IsValid(m.Version) {
			// 禁止fetch支持的范围更广的查询。查找。
			base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", m.Path, m.Version)
		}
		name = "go.mod"
		data, err = modfetch.GoMod(m.Path, m.Version)
	}
	return name, data, err
}

// QuerylatesVersionIgnoringRetractions使用给定路径查找
// 模块的最新版本，而不考虑已收回或已排除的
// 版本。
// 
// 如果模块的所有版本都被替换，
// QueryLastVersionIgnoringRetractions返回替换，而不进行查询。
// 
// 如果查询到的最新版本被替换，
// QueryLastVersionIgnoringRetractions返回替换。
func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (latest module.Version, err error) {
	type entry struct {
		latest module.Version
		err    error
	}
	e := latestVersionIgnoringRetractionsCache.Do(path, func() any {
		ctx, span := trace.StartSpan(ctx, "queryLatestVersionIgnoringRetractions "+path)
		defer span.Done()

		if repl := Replacement(module.Version{Path: path}); repl.Path != "" {
			// 模块的所有版本均已更换。
			// 不需要查询。
			return &entry{latest: repl}
		}

		// 查找模块的最新版本。
		// 忽略主模块go中的排除项。摩登派青年
		const ignoreSelected = ""
		var allowAll AllowedFunc
		rev, err := Query(ctx, path, "latest", ignoreSelected, allowAll)
		if err != nil {
			return &entry{err: err}
		}
		latest := module.Version{Path: path, Version: rev.Version}
		if repl := resolveReplacement(latest); repl.Path != "" {
			latest = repl
		}
		return &entry{latest: latest}
	}).(*entry)
	return e.latest, e.err
}

var latestVersionIgnoringRetractionsCache par.Cache // 路径→ QueryLastVersionIgnoringRetractions result 

// ToDirectoryPath在必要时添加前缀，以便明确的
// 中的路径是以“.”开头的绝对路径或相对路径或“…”
// 路径组件。
func ToDirectoryPath(path string) string {
	if path == "." || modfile.IsDirectoryPath(path) {
		return path
	}
	// 该路径不是相对路径或绝对路径，因此将其设置为相对于当前目录的
	// 路径。
	return "./" + filepath.ToSlash(filepath.Clean(path))
}
